/* =========================================
 *      文件存储子服务的客户端测试搭建 
 * =========================================*/

#include "base.pb.h"
#include "etcd.hpp"                 // 如果在后面包含, 会先引入<cctype>, 进而识别不出transform中使用的接口所在位置
#include "channel.hpp"
#include "file.pb.h"
#include "utils.hpp"
#include <brpc/controller.h>
#include <gflags/gflags.h>
#include <google/protobuf/map.h>
#include <gtest/gtest.h>
#include <string>
#include <chrono>
#include <thread>

DEFINE_bool(run_mode, false, "程序的运行模式: false-调试 true-发布");
DEFINE_string(log_file, "", "指定日志的输出文件");
DEFINE_int32(log_level, 0, "指定日志输出等级");

DEFINE_string(etcd_host, "http://127.0.0.1:2379", "服务注册中心地址");
DEFINE_string(base_service, "/service", "服务监控根目录");
DEFINE_string(file_service, "/service/file_service", "当前监控的服务");

DEFINE_string(storage_path, "./data/", "上传的文件数据的存储位置");

shared::infra::RpcServiceChannel::ChannelPtr channel;
std::string singleFileId;
std::vector<std::string> multiFileId;

// 上传单文件功能单元测试
TEST(put_test, single_file) 
{
    // 读取本地文件
    std::string body;
    ASSERT_TRUE(shared::FileUtil::read("./Makefile", body));

    // rpc远程调用发送请求
    im_proto::FileService_Stub stub(channel.get());
    im_proto::PutSingleFileReq req;
    req.set_request_id("20250714183517");
    req.mutable_file_data()->set_file_name("Makefile");
    req.mutable_file_data()->set_file_size(body.size());
    req.mutable_file_data()->set_file_content(body);

    // 获取响应
    brpc::Controller* controller = new brpc::Controller();
    im_proto::PutSingleFileRsp* resp = new im_proto::PutSingleFileRsp();
    stub.PutSingleFile(controller, &req, resp, nullptr);
    ASSERT_FALSE(controller->Failed());

    // 检测返回值中上传是否成功
    ASSERT_TRUE(resp->success());
    ASSERT_EQ(resp->file_info().file_size(), body.size());
    ASSERT_EQ(resp->file_info().file_name(), "Makefile");
    singleFileId = resp->file_info().file_id();   
    LOG_DEBUG("文件ID: {}", singleFileId);
}

// 获取单文件功能单元测试
TEST(get_test, single_file) 
{
    // rpc远程调用发送请求
    im_proto::FileService_Stub stub(channel.get());
    im_proto::GetSingleFileReq req;
    req.set_request_id("20250714185024");
    req.set_file_id(singleFileId);

    // 获取响应
    brpc::Controller* controller = new brpc::Controller();
    im_proto::GetSingleFileRsp* resp = new im_proto::GetSingleFileRsp();
    stub.GetSingleFile(controller, &req, resp, nullptr);
    ASSERT_FALSE(controller->Failed());
    ASSERT_TRUE(resp->success());

    // 将获取的数据存储到本地
    auto fileId = resp->file_data().file_id();
    ASSERT_EQ(singleFileId, fileId);
    shared::FileUtil::write("Makefile_download", resp->file_data().file_content());

    // 校验上传的文件与下载下来的文件的md5序列
    ASSERT_EQ(shared::FileUtil::getFileMd5Sum("Makefile_download"), 
            shared::FileUtil::getFileMd5Sum(FLAGS_storage_path + fileId));
}

// 上传多文件功能单元测试
TEST(put_test, multi_file) 
{
    // 读取本地文件
    std::string body1, body2;
    ASSERT_TRUE(shared::FileUtil::read("./base.pb.h", body1));
    ASSERT_TRUE(shared::FileUtil::read("./file.pb.h", body2));

    // rpc远程调用发送请求
    im_proto::FileService_Stub stub(channel.get());
    im_proto::PutMultiFileReq req;
    req.set_request_id("20250714185534");
    im_proto::FileUploadData* fileData = req.add_file_data();
    fileData->set_file_name("base.pb.h");
    fileData->set_file_size(body1.size());
    fileData->set_file_content(body1);

    fileData = req.add_file_data();
    fileData->set_file_name("file.pb.h");
    fileData->set_file_size(body2.size());
    fileData->set_file_content(body2);

    // 获取响应
    brpc::Controller* controller = new brpc::Controller();
    im_proto::PutMultiFileRsp* resp = new im_proto::PutMultiFileRsp();
    stub.PutMultiFile(controller, &req, resp, nullptr);
    ASSERT_FALSE(controller->Failed());

    // 检测返回值中上传是否成功
    ASSERT_TRUE(resp->success());
    for(const auto& fileMsg : resp->file_info()) {
        multiFileId.emplace_back(fileMsg.file_id());
        LOG_DEBUG("文件ID: {}", fileMsg.file_id());
    }
}

// 获取多文件功能单元测试
TEST(get_test, multi_file) 
{
    // rpc远程调用发送请求
    im_proto::FileService_Stub stub(channel.get());
    im_proto::GetMultiFileReq req;
    req.set_request_id("20250714190029");
    req.add_file_id_list(multiFileId[0]);
    req.add_file_id_list(multiFileId[1]);

    // 获取响应
    brpc::Controller* controller = new brpc::Controller();
    im_proto::GetMultiFileRsp* resp = new im_proto::GetMultiFileRsp();
    stub.GetMultiFile(controller, &req, resp, nullptr);
    ASSERT_FALSE(controller->Failed());
    ASSERT_TRUE(resp->success());

    // 将获取的数据存储到本地
    typedef const google::protobuf::Map<std::string, im_proto::FileDownloadData>& GPMap;
    GPMap fileData = resp->file_data();
    ASSERT_TRUE(fileData.find(multiFileId[0]) != fileData.end());
    ASSERT_TRUE(fileData.find(multiFileId[1]) != fileData.end());
    shared::FileUtil::write("base_download", fileData.at(multiFileId[0]).file_content());
    shared::FileUtil::write("file_download", fileData.at(multiFileId[1]).file_content());

    // 校验上传的文件与下载下来的文件的md5序列
    ASSERT_EQ(shared::FileUtil::getFileMd5Sum("base_download"), 
        shared::FileUtil::getFileMd5Sum(FLAGS_storage_path + multiFileId[0]));

    ASSERT_EQ(shared::FileUtil::getFileMd5Sum("file_download"), 
        shared::FileUtil::getFileMd5Sum(FLAGS_storage_path + multiFileId[1]));
}

int main(int argc, char* argv[])
{
    testing::InitGoogleTest(&argc, argv);
    google::ParseCommandLineFlags(&argc, &argv, true);
    shared::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);

    // 1.构造rpc服务管理对象
    shared::infra::RpcServiceManagerPtr rpcServiceMgr = std::make_shared<shared::infra::RpcServiceManager>();
    rpcServiceMgr->declared(FLAGS_file_service);   
    auto putCb = std::bind(
        &shared::infra::RpcServiceManager::onServiceOnline, rpcServiceMgr.get(), std::placeholders::_1, std::placeholders::_2
    );
    auto delCb = std::bind(
        &shared::infra::RpcServiceManager::onServiceOffline, rpcServiceMgr.get(), std::placeholders::_1, std::placeholders::_2
    );

    // 2.构造服务发现对象
    shared::infra::DiscoveryPtr client = std::make_shared<shared::infra::Discovery>(
        FLAGS_etcd_host, FLAGS_base_service, putCb, delCb
    );

    // 3.获取该服务的rpc信道对象
    channel = rpcServiceMgr->choose(FLAGS_file_service);
    if(!channel) {
        return 1;
    }

    return RUN_ALL_TESTS();
}